package cli

import (
	"go/ast"
	"go/types"
	"regexp"
	"strconv"
	"strings"

	"golang.org/x/tools/go/packages"
)

type ScanMate interface {
	//2.解析元信息,并保存进容器中
	ParseMate(pkgs []*packages.Package, meta map[string]interface{})
}

type BeanDefintionScanner struct{}

type FieldInjection struct {
	FieldName string
	BeanName  string
	TypeName  string
}

type ConfigInjection struct {
	FieldName string
	ConfigKey string
	GoType    string
}

type BeanDefinition struct {
	BeanName        string
	TypeName        string
	PackageName     string
	ImportPath      string
	FilePath        string
	Implements      []string
	AutoWiredFields []FieldInjection
	ConfigFields    []ConfigInjection
	ConfigPrefix    string
}

type AspectDefinition struct {
	BeanName   string
	TargetType string // 接口全路径
	Order      int    // 新增
}

func (b *BeanDefintionScanner) ParseMate(pkgs []*packages.Package, meta map[string]interface{}) {
	beanRegex := regexp.MustCompile(`@Bean(\(.*\))?`)
	beanNameRegex := regexp.MustCompile(`@Bean\s*\(\s*name\s*=\s*"([^"]+)"\s*\)`)
	configPropsRegex := regexp.MustCompile(`@ConfigurationProperties\s*\(\s*prefix\s*=\s*"([^"]+)"\s*\)`)
	aspectRegex := regexp.MustCompile(`@Aspect\s*\(\s*target\s*=\s*"([^"]+)"\s*\)`)
	orderRegex := regexp.MustCompile(`@Order\s*\(\s*([0-9]+)\s*\)`) // 新增
	autoWiredRegex := regexp.MustCompile(`@Autowired(\(.*\))?`)
	autoWiredNameRegex := regexp.MustCompile(`@Autowired\s*\(\s*name\s*=\s*"([^"]+)"\s*\)`)
	valueRegex := regexp.MustCompile(`@Value\s*\(\s*"\$\{([^}]+)\}"\s*\)`)

	var beans []BeanDefinition
	var aspects []AspectDefinition

	for _, pkg := range pkgs {
		if pkg.Types == nil || pkg.Syntax == nil {
			continue
		}

		interfaceMap := make(map[string]*types.Interface)
		addInterfacesFromScope(pkg.PkgPath, pkg.Types.Scope(), interfaceMap)
		for _, dep := range pkg.Imports {
			addInterfacesFromScope(dep.PkgPath, dep.Types.Scope(), interfaceMap)
		}

		for fileIdx, file := range pkg.Syntax {
			ast.Inspect(file, func(n ast.Node) bool {
				genDecl, ok := n.(*ast.GenDecl)
				if !ok {
					return true
				}
				for _, spec := range genDecl.Specs {
					typeSpec, ok := spec.(*ast.TypeSpec)
					if !ok {
						continue
					}
					structType, ok := typeSpec.Type.(*ast.StructType)
					if !ok {
						continue
					}

					beanName := ""
					isBean := false
					configPrefix := ""
					aspectTarget := ""
					aspOrder := 0 // 新增

					if genDecl.Doc != nil {
						for _, c := range genDecl.Doc.List {
							if beanRegex.MatchString(c.Text) {
								isBean = true
								if match := beanNameRegex.FindStringSubmatch(c.Text); len(match) > 1 {
									beanName = match[1]
								} else {
									beanName = strings.ToLower(typeSpec.Name.Name[:1]) + typeSpec.Name.Name[1:]
								}
							}
							if match := configPropsRegex.FindStringSubmatch(c.Text); len(match) > 1 {
								configPrefix = match[1]
							}
							if match := aspectRegex.FindStringSubmatch(c.Text); len(match) > 1 {
								aspectTarget = match[1]
							}
							if match := orderRegex.FindStringSubmatch(c.Text); len(match) > 1 {
								if order, err := strconv.Atoi(match[1]); err == nil {
									aspOrder = order
								}
							}
						}
					}

					if aspectTarget != "" && aspectTarget != "springboot/framework/container.Aspect" {
						aspects = append(aspects, AspectDefinition{
							BeanName:   beanName,
							TargetType: aspectTarget,
							Order:      aspOrder,
						})
						if beanName == "" {
							beanName = strings.ToLower(typeSpec.Name.Name[:1]) + typeSpec.Name.Name[1:]
							isBean = true
						}
					}

					if !isBean && configPrefix == "" {
						continue
					}

					beanDef := BeanDefinition{
						BeanName:     beanName,
						TypeName:     typeSpec.Name.Name,
						PackageName:  pkg.Name,
						ImportPath:   pkg.PkgPath,
						ConfigPrefix: configPrefix,
					}
					if fileIdx < len(pkg.CompiledGoFiles) {
						beanDef.FilePath = pkg.CompiledGoFiles[fileIdx]
					}

					// 检测实现接口
					if obj := pkg.Types.Scope().Lookup(typeSpec.Name.Name); obj != nil {
						ptrType := types.NewPointer(obj.Type())
						for ifaceName, ifaceType := range interfaceMap {
							if types.Implements(ptrType, ifaceType) {
								beanDef.Implements = append(beanDef.Implements, ifaceName)
							}
						}
					}

					// 扫描字段注入
					for _, field := range structType.Fields.List {
						fieldName := ""
						if len(field.Names) > 0 {
							fieldName = field.Names[0].Name
						}

						// @Autowired
						if field.Doc != nil {
							for _, c := range field.Doc.List {
								if autoWiredRegex.MatchString(c.Text) {
									awBeanName := ""
									if match := autoWiredNameRegex.FindStringSubmatch(c.Text); len(match) > 1 {
										awBeanName = match[1]
									} else {
										awBeanName = strings.ToLower(fieldName[:1]) + fieldName[1:]
									}
									beanDef.AutoWiredFields = append(beanDef.AutoWiredFields, FieldInjection{
										FieldName: fieldName,
										BeanName:  awBeanName,
										TypeName:  typeString(field.Type, pkg),
									})
								}
								// @Value("${...}")
								if match := valueRegex.FindStringSubmatch(c.Text); len(match) > 1 {
									beanDef.ConfigFields = append(beanDef.ConfigFields, ConfigInjection{
										FieldName: fieldName,
										ConfigKey: match[1],
										GoType:    typeString(field.Type, pkg),
									})
								}
							}
						}
					}

					beans = append(beans, beanDef)
				}
				return true
			})
		}
	}

	//注册到元信息 中
	meta["beans"] = beans
	meta["aspects"] = aspects
}

func addInterfacesFromScope(pkgPath string, scope *types.Scope, interfaceMap map[string]*types.Interface) {
	if scope == nil {
		return
	}
	for _, name := range scope.Names() {
		if obj := scope.Lookup(name); obj != nil {
			if typ, ok := obj.Type().Underlying().(*types.Interface); ok {
				fullName := pkgPath + "." + obj.Name()
				interfaceMap[fullName] = typ
			}
		}
	}
}

func typeString(expr ast.Expr, pkg *packages.Package) string {
	tv := pkg.TypesInfo.Types[expr]
	return types.TypeString(tv.Type, func(p *types.Package) string {
		return p.Name()
	})
}
